home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / PROGRAMM / DB_CLIPP / 0277.ZIP / SQ2.C < prev    next >
Text File  |  1986-02-28  |  36KB  |  1,418 lines

  1. static char *sccsid = "@(#)sq2.c        1.9u (UCF) 86/02/14";
  2. /*
  3.  *   sq2.c - CP/M compatible file squeezer utility
  4.  *
  5.  */
  6.  
  7.  
  8. #include <stdio.h>
  9.  
  10. #ifdef MSDC86
  11. #include <time.h>
  12. #define FBRDWR    "wrb"
  13. #define FBRD    "rb"
  14. #endif
  15.  
  16. #ifdef CPMC86
  17. #define FBRDWR    "wrb"
  18. #define FBRD    "rb"
  19. #define strupr    upper
  20. #endif
  21.  
  22. #ifdef MSDMSC
  23. #include <time.h>
  24. #include <dos.h>
  25. #include <fcntl.h>
  26. #include <sys\types.h>
  27. #include <sys\stat.h>
  28. #include <io.h>
  29. #define BWRITE    O_RDWR|O_CREAT|O_BINARY
  30. #define BPERMS    S_IREAD|S_IWRITE
  31. #define BREAD    O_RDONLY|O_BINARY
  32. #define FBRDWR    "w+b"
  33. #define FBRD    "rb"
  34. #endif
  35.  
  36. #define TRUE 1
  37. #define FALSE 0
  38. #define ERROR (-1)
  39. #define PATHLEN 64    /* Number of characters allowed in pathname */
  40.  
  41. /* Definitions and external declarations */
  42.  
  43. #define RECOGNIZE 0xFF76    /* unlikely pattern */
  44.  
  45. /* *** Stuff for first translation module *** */
  46.  
  47. #define DLE 0x90
  48.  
  49.  
  50. /* *** Stuff for second translation module *** */
  51.  
  52. #define SPEOF 256    /* special endfile token */
  53. #define NUMVALS 257    /* 256 data values plus SPEOF*/
  54.  
  55. typedef int INT;
  56. typedef unsigned UNSIGNED;
  57.  
  58. /* Definitions and external declarations */
  59.  
  60. INT Usestd;    /* Use stdout for squeezed output */
  61. UNSIGNED crc;    /* error check code */
  62.  
  63. /* *** Stuff for first translation module *** */
  64.  
  65. INT likect;    /*count of consecutive identical chars */
  66. INT lastchar, newchar;
  67. unsigned char state;
  68.  
  69. /* states */
  70.  
  71. #define NOHIST    0    /*don't consider previous input*/
  72. #define SENTCHAR 1    /*lastchar set, no lookahead yet */
  73. #define SENDNEWC 2    /*newchar set, previous sequence done */
  74. #define SENDCNT 3    /*newchar set, DLE sent, send count next */
  75.  
  76. /* *** Stuff for second translation module *** */
  77.  
  78. #define NOCHILD -1    /* indicates end of path through tree */
  79. #define NUMNODES (NUMVALS + NUMVALS - 1)    /* nbr of nodes */
  80.  
  81. #define MAXCOUNT (UNSIGNED) 65535    /* biggest UNSIGNED integer */
  82.  
  83. /* The following array of structures are the nodes of the
  84.  * binary trees. The first NUMVALS nodes becomethe leaves of the
  85.  * final tree and represent the values of the data bytes being
  86.  * encoded and the special endfile, SPEOF.
  87.  * The remaining nodes become the internal nodes of the final tree.
  88.  */
  89.  
  90. struct    nd {
  91.     UNSIGNED weight;    /* number of appearances */
  92.     INT tdepth;        /* length on longest path in tre*/
  93.     INT lchild, rchild;    /* indexes to next level */
  94. } node[NUMNODES];
  95.  
  96. INT dctreehd;    /*index to head node of final tree */
  97.  
  98. /* This is the encoding table:
  99.  * The bit strings have first bit in  low bit.
  100.  * Note that counts were scaled so code fits UNSIGNED integer
  101.  */
  102.  
  103. INT codelen[NUMVALS];        /* number of bits in code */
  104. UNSIGNED code[NUMVALS];     /* code itself, right adjusted */
  105. UNSIGNED tcode;         /* temporary code value */
  106.  
  107.  
  108. /* Variables used by encoding process */
  109.  
  110. INT curin;    /* Value currently being encoded */
  111. INT cbitsrem;    /* Number of code string bits remaining */
  112. UNSIGNED ccode; /* Current code shifted so next code bit is at right */
  113.  
  114. /* This program compresses a file without losing information.
  115.  * The usq2.com program is required to unsqueeze the file
  116.  * before it can be used.
  117.  *
  118.  * Typical compression rates are:
  119.  *    .COM    6%    (Don't bother)
  120.  *    .ASM    33%    (using full ASCII set)
  121.  *    .DIC    46%    (using only uppercase and a few others)
  122.  * Squeezing a really big file takes a few minutes.
  123.  *
  124.  * Useage:
  125.  *    sq2 file ...
  126.  *
  127.  *
  128.  * The squeezed file name is formed by changing the second from last
  129.  * letter of the file type to Q. If there is no file type,
  130.  * the squeezed file type is QQQ. If the name exists it is
  131.  * overwritten!
  132.  *
  133.  * The transformations compress strings of identical bytes and
  134.  * then encode each resulting byte value and EOF as bit strings
  135.  * having lengths in inverse proportion to their frequency of
  136.  * occurrence in the intermediate input stream. The latter uses
  137.  * the Huffman algorithm. Decoding information is included in
  138.  * the squeezed file, so squeezing short files or files with
  139.  * uniformly distributed byte values will actually increase size.
  140.  */
  141.  
  142. /* CHANGE HISTORY:
  143.  * 1.5u **nix version - means output to stdout.
  144.  *  (stdin not allowed becuase sq needs to rewind input, which
  145.  *  won't work with pipes.)
  146.  * Filename generation changed to suit **nix and stdio.
  147.  * 1.6u machine independent output for file compatibility with
  148.  *    original CP/M version SQ, when running on machine with
  149.  *    IBM byte ordering such as Z8000 and 68000.
  150.  * 1.7u machine independence was still lacking for 32-bit machines
  151.  *    like the VAX-11/780, so a typedef was added.  No action
  152.  *    need be taken if running on a 16-bit machine, but if
  153.  *    running on a VAX, define VAX either on the cc line or
  154.  *    in the program preamble.   Ben Goldfarb 12/13/82
  155.  * 1.8u Modified to run under CI-86 compiler for the IBM PC
  156.  *    Robert J. Beilstein 09/02/83
  157.  * 1.9u CI-86 source modified to squeeze files to floppy disk which
  158.  *    are larger than the floppy disk. Similar to BACKUP.COM.
  159.  *    Ark Software 07.01.86
  160.  *    Code for MSC and CPM86 - 23.02.86
  161.  *
  162.  */
  163.  
  164. #define VERSION "1.9u   14-02-86"
  165.  
  166. INT inbackground = 0;  /* change to 1 to suppress informative messages */
  167.  
  168. INT buildenc(), gethuff(), getc_crc();
  169.  
  170. /* new globals for version 1.9u
  171.    ---------------------------- */
  172. long free_space = 0L;
  173. INT bkup_id = 0;
  174. INT span_cnt = 0;
  175. char tgt_drv[20];
  176. unsigned char outfile[PATHLEN+2];     /* output file spec. */
  177. unsigned char infile[PATHLEN+2];
  178. FILE *inbuff, *outbuff;          /* file buffers */
  179.  
  180. #define SPAN_CHR    0xff        /* marks incomplete restore/file */
  181. #define FIN_CHR     0x00        /* marks last disk/last segment */
  182.  
  183. /* structure of SQZID.@@@ */
  184.  
  185. struct bki {
  186.     unsigned char bk_fflag;        /* FF=middle disk 00=last disk */
  187.     int       bk_seqn;        /* disk sequence number */
  188.     long      bk_date;        /* backup date in long format - seconds since 01.01.70 */
  189.     char      bk_spare[121];
  190.     } bkid;
  191.  
  192. /* structure of SQUEEZED file header */
  193.  
  194. struct sqh {
  195.     unsigned char sq_fflag;        /* FF=incomplete 00=last segment */
  196.     int       sq_span;        /* span count */
  197.     int       sq_nulo;
  198.     char      sq_path[65];        /* path + filename */
  199.     char      sq_spare[58];     /* spare */
  200.     } sqid;
  201.  
  202. #define FNSZE    81
  203.  
  204. /* structure of file info block from wildexp modules */
  205.  
  206. struct    fil_info {
  207.     unsigned attrib;            /* attribute found (int for future UNIX) */
  208.     unsigned ftime;             /* file time MSDOS format */
  209.     unsigned fdate;             /* file date MSDOS format */
  210.     long fsize;
  211.     char fil_nam[FNSZE];            /* ASCIIZ file name - maximum which is not all allocated by sbrk */
  212.     };
  213.  
  214. struct fil_info *finfo;
  215.  
  216. long pspace();
  217.  
  218. main(argc, argv)
  219. INT argc;
  220. char *argv[];
  221. {
  222.     register INT i,c;
  223.  
  224.     if(argc < 3)
  225.         usage();
  226.     strcpy(tgt_drv, argv[argc - 1]);
  227.     if(toupper(tgt_drv[0]) > 'B' || toupper(tgt_drv[0]) < 'A')
  228.         abort("ERR - target must be drive A: or drive B:");
  229.     if(hasbad(tgt_drv))
  230.         usage();
  231.     strupr(tgt_drv);
  232.     argc--;
  233.  
  234. #ifdef MSDC86
  235.     if (wildexp(&argc,&argv,0) == ERROR)
  236.         {
  237.         abort("ERR - wildcard extraction error\n\n\n");
  238.         }
  239.     time(&bkid.bk_date);
  240. #endif
  241.  
  242. #ifdef CPMC86
  243.     if (wildexp(&argc,&argv) == ERROR)
  244.         {
  245.         abort("ERR - wildcard extraction error\n\n\n");
  246.         }
  247.     bkid.bk_date = 123456L;
  248. #endif
  249.  
  250.     /* Process the parameters in order */
  251.  
  252. #ifdef MSDC86
  253.     for(i = 1; i < argc; ++i)
  254.         {
  255.         finfo = (struct fil_info *) *++argv;
  256.         obey(finfo->fil_nam);
  257.         }
  258. #else
  259.     for(i = 1; i < argc; ++i)
  260.         {
  261.         obey(*++argv);
  262.         }
  263. #endif
  264.  
  265.     exit(0);
  266. }
  267.  
  268. obey(p)
  269. unsigned char *p;
  270. {
  271.     unsigned char *w,*q;
  272.     extern char *strrchr();
  273.  
  274.     /* First build output file name */
  275.  
  276.     strcpy(outfile, tgt_drv);
  277.     span_cnt = 0;
  278.     w = p;
  279.     if((q = strrchr(w,'/')) != NULL)
  280.         w = q + 1;
  281.     if((q = strrchr(w,'\\')) != NULL)
  282.         w = q + 1;
  283.     if((q = strrchr(w,':')) != NULL)
  284.         w = q + 1;
  285.  
  286.     strcat(outfile, w);
  287.     if((q = strrchr(outfile,'.')) != NULL)
  288.         if(*(q+1) == '\0')
  289.         q = '\0';
  290.     strcpy(infile,p);
  291.     strupr(infile);
  292.     squeeze();
  293. }
  294.  
  295. squeeze()
  296. {
  297.     register INT i, c;
  298.     long fseek();
  299.  
  300.     /* enough room for "RESTORE" header and more? */
  301.     if(free_space < 512L)
  302.     new_disk();
  303.  
  304.     if (!inbackground)
  305.     fprintf(stderr, "%s -> %s: ", infile, outfile);
  306.  
  307.     if((inbuff=fopen(infile, FBRD)) == NULL)
  308.     {
  309.     fprintf(stderr, "sq2: can't open %s\n", infile);
  310.     return;
  311.     }
  312.  
  313.  
  314.     if((outbuff=fopen(outfile, FBRDWR)) == NULL)
  315.     {
  316.     fprintf(stderr, "sq2: can't create %s\n", outfile);
  317.     fclose(inbuff);
  318.     return;
  319.     }
  320.  
  321.     /* First pass - get properties of file */
  322.     crc = 0;        /* initialize checksum */
  323.     if (!inbackground)
  324.     fprintf(stderr, "analyzing, ");
  325.     init_ncr();
  326.  
  327.     /* analyse file - make code-decode tree */
  328.     init_huff(inbuff);
  329.  
  330.     /* rewind file */
  331.     fseek(inbuff,0L,0);
  332.  
  333.     /* write out "RESTORE" header */
  334.     wrt_sqhead(infile);
  335.  
  336.     /* Write output file header with decoding info */
  337.     wrt_head(infile);
  338.  
  339.     /* Second pass - encode the file */
  340.     if (!inbackground)
  341.     fprintf(stderr, "squeezing, ");
  342.  
  343.     init_ncr();     /* For second pass */
  344.  
  345.     /* Translate the input file into the output file */
  346.     while((c = gethuff(inbuff)) != EOF)
  347.     {
  348.     if(putc(c, outbuff) == ERROR && ferror(outbuff))
  349.         {
  350.         abort("\nsq2: write error\n");
  351.         }
  352.     if(--free_space == 0L)
  353.         {
  354.         span_cnt++;
  355.         new_disk();
  356.         }
  357.     }
  358.     if (!inbackground)
  359.     fprintf(stderr, " done.\n");
  360. closeall:
  361.     fclose(inbuff);
  362. closeout:
  363.     fflush(outbuff);
  364.     fclose(outbuff);
  365.     free_space = pspace(tgt_drv);
  366. }
  367.  
  368.  
  369. /* First translation - encoding of repeated characters
  370.  * The code is byte for byte pass through except that
  371.  * DLE is encoded as DLE, zero and repeated byte values
  372.  * are encoded as value, DLE, count for count >= 3.
  373.  */
  374.  
  375. init_ncr()    /*initialize getcnr() */
  376. {
  377.     state = NOHIST;
  378. }
  379.  
  380. INT
  381. getcnr(iob)
  382. FILE *iob;
  383. {
  384.     switch(state) {
  385.     case NOHIST:
  386.         /* No relevant history */
  387.         state = SENTCHAR;
  388.         return lastchar = getc_crc(iob);
  389.     case SENTCHAR:
  390.         /* Lastchar is set, need lookahead */
  391.         switch(lastchar) {
  392.         case DLE:
  393.             state = NOHIST;
  394.             return 0;    /* indicates DLE was the data */
  395.         case EOF:
  396.             return EOF;
  397.         default:
  398.             for(likect = 1; (newchar = getc_crc(iob)) == lastchar && likect < 255; ++likect)
  399.                 ;
  400.             switch(likect) {
  401.             case 1:
  402.                 return lastchar = newchar;
  403.             case 2:
  404.                 /* just pass through */
  405.                 state = SENDNEWC;
  406.                 return lastchar;
  407.             default:
  408.                 state = SENDCNT;
  409.                 return DLE;
  410.             }
  411.         }
  412.     case SENDNEWC:
  413.         /* Previous sequence complete, newchar set */
  414.         state = SENTCHAR;
  415.         return lastchar = newchar;
  416.     case SENDCNT:
  417.         /* Sent DLE for repeat sequence, send count */
  418.         state = SENDNEWC;
  419.         return likect;
  420.     default:
  421.         fprintf(stderr,"sq2: Bug - bad state\n");
  422.         exit(1);
  423.         /* NOTREACHED */
  424.     }
  425. }
  426.  
  427.  
  428. /******** Second translation - bytes to variable length bit strings *********/
  429.  
  430.  
  431. /* This translation uses the Huffman algorithm to develop a
  432.  * binary tree representing the decoding information for
  433.  * a variable length bit string code for each input value.
  434.  * Each string's length is in inverse proportion to its
  435.  * frequency of appearance in the incoming data stream.
  436.  * The encoding table is derived from the decoding table.
  437.  *
  438.  * The range of valid values into the Huffman algorithm are
  439.  * the values of a byte stored in an integer plus the special
  440.  * endfile value chosen to be an adjacent value. Overall, 0-SPEOF.
  441.  *
  442.  * The "node" array of structures contains the nodes of the
  443.  * binary tree. The first NUMVALS nodes are the leaves of the
  444.  * tree and represent the values of the data bytes being
  445.  * encoded and the special endfile, SPEOF.
  446.  * The remaining nodes become the internal nodes of the tree.
  447.  *
  448.  * In the original design it was believed that
  449.  * a Huffman code would fit in the same number of
  450.  * bits that will hold the sum of all the counts.
  451.  * That was disproven by a user's file and was a rare but
  452.  * infamous bug. This version attempts to choose among equally
  453.  * weighted subtrees according to their maximum depths to avoid
  454.  * unnecessarily long codes. In case that is not sufficient
  455.  * to guarantee codes <= 16 bits long, we initially scale
  456.  * the counts so the total fits in an unsigned integer, but
  457.  * if codes longer than 16 bits are generated the counts are
  458.  * rescaled to a lower ceiling and code generation is retried.
  459.  */
  460.  
  461. /* Initialize the Huffman translation. This requires reading
  462.  * the input file through any preceding translation functions
  463.  * to get the frequency distribution of the various values.
  464.  */
  465.  
  466. init_huff(ib)
  467. FILE *ib;
  468. {
  469.     register INT c, i;
  470.     INT btlist[NUMVALS];    /* list of intermediate binary trees */
  471.     INT listlen;        /* length of btlist */
  472.     UNSIGNED *wp;        /* simplifies weight counting */
  473.     UNSIGNED ceiling;    /* limit for scaling */
  474.  
  475.     /* Initialize tree nodes to no weight, no children */
  476.     init_tree();
  477.  
  478.     /* Build frequency info in tree */
  479.     do {
  480.         c = getcnr(ib);
  481.         if(c == EOF)
  482.             c = SPEOF;
  483.         if(*(wp = &node[c].weight) !=  MAXCOUNT)
  484.             ++(*wp);
  485.     }
  486.     while(c != SPEOF);
  487.  
  488.     ceiling = MAXCOUNT;
  489.  
  490.     do {    /* Keep trying to scale and encode */
  491.         if(ceiling != MAXCOUNT)
  492.             fprintf(stderr, "sq2: *** rescaling ***, ");
  493.         scale(ceiling);
  494.         ceiling /= 2;    /* in case we rescale */
  495.  
  496.         /* Build list of single node binary trees having
  497.                  * leaves for the input values with non-zero counts
  498.                  */
  499.         for(i = listlen = 0; i < NUMVALS; ++i)
  500.             if(node[i].weight != 0) {
  501.                 node[i].tdepth = 0;
  502.                 btlist[listlen++] = i;
  503.             }
  504.  
  505.         /* Arrange list of trees into a heap with the entry
  506.                  * indexing the node with the least weight at the top.
  507.                  */
  508.         heap(btlist, listlen);
  509.  
  510.         /* Convert the list of trees to a single decoding tree */
  511.         bld_tree(btlist, listlen);
  512.  
  513.         /* Initialize the encoding table */
  514.         init_enc();
  515.  
  516.         /* Try to build encoding table.
  517.                  * Fail if any code is > 16 bits long.
  518.                  */
  519.     }
  520.     while(buildenc(0, dctreehd) == ERROR);
  521.  
  522.     /* Initialize encoding variables */
  523.     cbitsrem = 0;    /*force initial read */
  524.     curin = 0;    /*anything but endfile*/
  525. }
  526.  
  527. /* The count of number of occurrances of each input value
  528.  * have already been prevented from exceeding MAXCOUNT.
  529.  * Now we must scale them so that their sum doesn't exceed
  530.  * ceiling and yet no non-zero count can become zero.
  531.  * This scaling prevents errors in the weights of the
  532.  * interior nodes of the Huffman tree and also ensures that
  533.  * the codes will fit in an unsigned integer. Rescaling is
  534.  * used if necessary to limit the code length.
  535.  */
  536.  
  537. scale(ceil)
  538. UNSIGNED ceil;    /* upper limit on total weight */
  539. {
  540.     register INT i,c;
  541.     INT ovflw, divisor;
  542.     UNSIGNED w, sum;
  543.     unsigned char increased;     /* flag */
  544.  
  545.     do {
  546.         for(i = sum = ovflw = 0; i < NUMVALS; ++i) {
  547.             if(node[i].weight > (ceil - sum))
  548.                 ++ovflw;
  549.             sum += node[i].weight;
  550.         }
  551.  
  552.         divisor = ovflw + 1;
  553.  
  554.         /* Ensure no non-zero values are lost */
  555.         increased = FALSE;
  556.         for(i = 0; i < NUMVALS; ++i) {
  557.             w = node[i].weight;
  558.             if (w < divisor && w != 0) {
  559.                 /* Don't fail to provide a code if it's used at all */
  560.                 node[i].weight = divisor;
  561.                 increased = TRUE;
  562.             }
  563.         }
  564.     }
  565.     while(increased);
  566.  
  567.     /* Scaling factor choosen, now scale */
  568.     if(divisor > 1)
  569.         for(i = 0; i < NUMVALS; ++i)
  570.             node[i].weight /= divisor;
  571. }
  572.  
  573. /* heap() and adjust() maintain a list of binary trees as a
  574.  * heap with the top indexing the binary tree on the list
  575.  * which has the least weight or, in case of equal weights,
  576.  * least depth in its longest path. The depth part is not
  577.  * strictly necessary, but tends to avoid long codes which
  578.  * might provoke rescaling.
  579.  */
  580.  
  581. heap(list, length)
  582. INT list[], length;
  583. {
  584.     register INT i;
  585.  
  586.     for(i = (length - 2) / 2; i >= 0; --i)
  587.         adjust(list, i, length - 1);
  588. }
  589.  
  590. /* Make a heap from a heap with a new top */
  591.  
  592. adjust(list, top, bottom)
  593. INT list[], top, bottom;
  594. {
  595.     register INT k, temp;
  596.  
  597.     k = 2 * top + 1;    /* left child of top */
  598.     temp = list[top];    /* remember root node of top tree */
  599.     if( k <= bottom) {
  600.         if( k < bottom && cmptrees(list[k], list[k + 1]))
  601.             ++k;
  602.  
  603.         /* k indexes "smaller" child (in heap of trees) of top */
  604.         /* now make top index "smaller" of old top and smallest child */
  605.         if(cmptrees(temp, list[k])) {
  606.             list[top] = list[k];
  607.             list[k] = temp;
  608.             /* Make the changed list a heap */
  609.             adjust(list, k, bottom); /*recursive*/
  610.         }
  611.     }
  612. }
  613.  
  614. /* Compare two trees, if a > b return true, else return false
  615.  * note comparison rules in previous comments.
  616.  */
  617.  
  618. cmptrees(a, b)
  619. INT a, b;    /* root nodes of trees */
  620. {
  621.     if(node[a].weight > node[b].weight)
  622.         return TRUE;
  623.     if(node[a].weight == node[b].weight)
  624.         if(node[a].tdepth > node[b].tdepth)
  625.             return TRUE;
  626.     return FALSE;
  627. }
  628.  
  629. /* HUFFMAN ALGORITHM: develops the single element trees
  630.  * into a single binary tree by forming subtrees rooted in
  631.  * interior nodes having weights equal to the sum of weights of all
  632.  * their descendents and having depth counts indicating the
  633.  * depth of their longest paths.
  634.  *
  635.  * When all trees have been formed into a single tree satisfying
  636.  * the heap property (on weight, with depth as a tie breaker)
  637.  * then the binary code assigned to a leaf (value to be encoded)
  638.  * is then the series of left (0) and right (1)
  639.  * paths leading from the root to the leaf.
  640.  * Note that trees are removed from the heaped list by
  641.  * moving the last element over the top element and
  642.  * reheaping the shorter list.
  643.  */
  644.  
  645. bld_tree(list, len)
  646. INT list[];
  647. INT len;
  648. {
  649.     register INT freenode;        /* next free node in tree */
  650.     register struct nd *frnp;    /* free node pointer */
  651.     INT lch, rch;        /* temporaries for left, right children */
  652.     INT i;
  653.  
  654.     /* Initialize index to next available (non-leaf) node.
  655.          * Lower numbered nodes correspond to leaves (data values).
  656.          */
  657.     freenode = NUMVALS;
  658.  
  659.     while(len > 1) {
  660.         /* Take from list two btrees with least weight
  661.                  * and build an interior node pointing to them.
  662.                  * This forms a new tree.
  663.                  */
  664.         lch = list[0];    /* This one will be left child */
  665.  
  666.         /* delete top (least) tree from the list of trees */
  667.         list[0] = list[--len];
  668.         adjust(list, 0, len - 1);
  669.  
  670.         /* Take new top (least) tree. Reuse list slot later */
  671.         rch = list[0];    /* This one will be right child */
  672.  
  673.         /* Form new tree from the two least trees using
  674.                  * a free node as root. Put the new tree in the list.
  675.                  */
  676.         frnp = &node[freenode]; /* address of next free node */
  677.         list[0] = freenode++;    /* put at top for now */
  678.         frnp->lchild = lch;
  679.         frnp->rchild = rch;
  680.         frnp->weight = node[lch].weight + node[rch].weight;
  681.         frnp->tdepth = 1 + maxchar(node[lch].tdepth, node[rch].tdepth);
  682.         /* reheap list    to get least tree at top*/
  683.         adjust(list, 0, len - 1);
  684.     }
  685.     dctreehd = list[0];    /*head of final tree */
  686. }
  687.  
  688. /* ???????????? */
  689. maxchar(a, b)
  690. {
  691.     return a > b ? a : b;
  692. }
  693. /* Initialize all nodes to single element binary trees
  694.  * with zero weight and depth.
  695.  */
  696.  
  697. init_tree()
  698. {
  699.     register INT i;
  700.  
  701.     for(i = 0; i < NUMNODES; ++i) {
  702.         node[i].weight = 0;
  703.         node[i].tdepth = 0;
  704.         node[i].lchild = NOCHILD;
  705.         node[i].rchild = NOCHILD;
  706.     }
  707. }
  708.  
  709. init_enc()
  710. {
  711.     register INT i;
  712.  
  713.     /* Initialize encoding table */
  714.     for(i = 0; i < NUMVALS; ++i) {
  715.         codelen[i] = 0;
  716.     }
  717. }
  718.  
  719. /* Recursive routine to walk the indicated subtree and level
  720.  * and maintain the current path code in bstree. When a leaf
  721.  * is found the entire code string and length are put into
  722.  * the encoding table entry for the leaf's data value .
  723.  *
  724.  * Returns ERROR if codes are too long.
  725.  */
  726.  
  727. INT        /* returns ERROR or NULL */
  728. buildenc(level, root)
  729. INT level;/* level of tree being examined, from zero */
  730. INT root; /* root of subtree is also data value if leaf */
  731. {
  732.     register INT l, r;
  733.     unsigned int shifty = 0xffff;
  734.  
  735.     l = node[root].lchild;
  736.     r = node[root].rchild;
  737.  
  738.     if( l == NOCHILD && r == NOCHILD) {
  739.         /* Leaf. Previous path determines bit string
  740.                  * code of length level (bits 0 to level - 1).
  741.                  * Ensures unused code bits are zero.
  742.                  */
  743.         codelen[root] = level;
  744.         code[root] = tcode & (shifty >> (16 - level));
  745.         return (level > 16) ? ERROR : NULL;
  746.     }
  747.     else {
  748.         if( l != NOCHILD) {
  749.             /* Clear path bit and continue deeper */
  750.             tcode &= ~(1 << level);
  751.             /* NOTE RECURSION */
  752.             if(buildenc(level + 1, l) == ERROR)
  753.                 return ERROR;
  754.         }
  755.         if(r != NOCHILD) {
  756.             /* Set path bit and continue deeper */
  757.             tcode |= 1 << level;
  758.             /* NOTE RECURSION */
  759.             if(buildenc(level + 1, r) == ERROR)
  760.                 return ERROR;
  761.         }
  762.     }
  763.     return NULL;    /* if we got here we're ok so far */
  764. }
  765.  
  766. /* Write out the sq header of the compressed file */
  767.  
  768. wrt_head(infile)
  769. char *infile;
  770. {
  771.     register INT l,r;
  772.     INT i, k;
  773.     INT numnodes;        /* nbr of nodes in simplified tree */
  774.  
  775.     putwe(RECOGNIZE);        /* identifies as compressed */
  776.     putwe(crc);            /* unsigned sum of original data */
  777.  
  778.     /* Write out a simplified decoding tree. Only the interior
  779.          * nodes are written. When a child is a leaf index
  780.          * (representing a data value) it is recoded as
  781.          * -(index + 1) to distinguish it from interior indexes
  782.          * which are recoded as positive indexes in the new tree.
  783.          * Note that this tree will be empty for an empty file.
  784.          */
  785.  
  786.     numnodes = dctreehd < NUMVALS ? 0 : dctreehd - (NUMVALS -1);
  787.     putwe(numnodes);
  788.  
  789.     for(k = 0, i = dctreehd; k < numnodes; ++k, --i) {
  790.         l = node[i].lchild;
  791.         r = node[i].rchild;
  792.         l = l < NUMVALS ? -(l + 1) : dctreehd - l;
  793.         r = r < NUMVALS ? -(r + 1) : dctreehd - r;
  794.         putwe(l);        /* left child */
  795.         putwe(r);        /* right child */
  796.     }
  797. }
  798.  
  799. /* Get an encoded byte or EOF. Reads from specified stream AS NEEDED.
  800.  *
  801.  * There are two unsynchronized bit-byte relationships here.
  802.  * The input stream bytes are converted to bit strings of
  803.  * various lengths via the static variables named c...
  804.  * These bit strings are concatenated without padding to
  805.  * become the stream of encoded result bytes, which this
  806.  * function returns one at a time. The EOF (end of file) is
  807.  * converted to SPEOF for convenience and encoded like any
  808.  * other input value. True EOF is returned after that.
  809.  *
  810.  * The original gethuff() called a seperate function,
  811.  * getbit(), but that more readable version was too slow.
  812.  */
  813.  
  814. INT        /*  Returns byte values except for EOF */
  815. gethuff(ib)
  816. FILE *ib;
  817. {
  818.     INT rbyte;    /* Result byte value */
  819.     INT need, take; /* numbers of bits */
  820.  
  821.     rbyte = 0;
  822.     need = 8;    /* build one byte per call */
  823.  
  824.     /* Loop to build a byte of encoded data
  825.          * Initialization forces read the first time
  826.          */
  827.  
  828. loop:
  829.     if(cbitsrem >= need) {
  830.         /* Current code fullfills our needs */
  831.         if(need == 0)
  832.             return rbyte;
  833.         /* Take what we need */
  834.         rbyte |= ccode << (8 - need);
  835.         /* And leave the rest */
  836.         ccode >>= need;
  837.         cbitsrem -= need;
  838.         return rbyte & 0xff;
  839.     }
  840.  
  841.     /* We need more than current code */
  842.     if(cbitsrem > 0) {
  843.         /* Take what there is */
  844.         rbyte |= ccode << (8 - need);
  845.         need -= cbitsrem;
  846.     }
  847.     /* No more bits in current code string */
  848.     if(curin == SPEOF) {
  849.         /* The end of file token has been encoded. If
  850.                  * result byte has data return it and do EOF next time
  851.                  */
  852.         cbitsrem = 0;
  853.  
  854.         /*NOTE: +0 is to fight compiler bug? */
  855.         return (need == 8) ? EOF : rbyte + 0;
  856.     }
  857.  
  858.     /* Get an input byte */
  859.     if((curin = getcnr(ib)) == EOF)
  860.         curin = SPEOF;    /* convenient for encoding */
  861.  
  862.     /* Get the new byte's code */
  863.     ccode = code[curin];
  864.     cbitsrem = codelen[curin];
  865.  
  866.     goto loop;
  867. }
  868.  
  869.  
  870. /* Get next byte from file and update checksum */
  871.  
  872. INT
  873. getc_crc(ib)
  874. FILE *ib;
  875. {
  876.     register INT c;
  877.  
  878.     c = getc(ib);
  879.     if(c != EOF)
  880.         crc += c;    /* checksum */
  881.     return c;
  882. }
  883.  
  884. /* Output functions with error reporting */
  885.  
  886. putce(c)
  887. INT c;
  888. {
  889.     if(putc(c, outbuff) == ERROR && ferror(outbuff)) {
  890.         abort("\nsq2: write error\n");
  891.         }
  892.     if(--free_space == 0L)
  893.         {
  894.         span_cnt++;
  895.         if(!new_disk())
  896.         abort("\nsq2: write error");
  897.         }
  898. }
  899.  
  900. /*
  901.  * machine independent put-word that writes low order byte first
  902.  *  (compatible with CP/M original) regardless of host cpu.
  903.  */
  904. putwe(w)
  905. INT w;
  906. {
  907.     putc(w, outbuff);
  908.     if(--free_space == 0L)
  909.         {
  910.         span_cnt++;
  911.         if(!new_disk())
  912.         abort("\nsq2: write error");
  913.         }
  914.     putc(w>>8, outbuff);
  915.     if(--free_space == 0L)
  916.         {
  917.         span_cnt++;
  918.         if(!new_disk())
  919.         abort("\nsq2: write error");
  920.         }
  921. }
  922.  
  923. /* ******************** new functions for ver. 1.9u ******************** */
  924.  
  925. usage()
  926. {
  927.     fprintf(stderr,"\n\nFile squeezer version %s by\n\n\tRichard Greenlaw\n\t251 Colony Ct.\n\tGahanna, Ohio 43230\n", VERSION);
  928.     fprintf(stderr,"\n\tMulti-disk version by Ark Software for the Public Domain");
  929.     fprintf(stderr,"\n\t     **this program is free and not for sale**\n\n");
  930.     fprintf(stderr, "Usage: sq2 <filename> ... <filename> <target drive>\n\n");
  931.     fprintf(stderr, "i.e.: sq2 phial*.dat phial*.idx grep.c b:\n\n");
  932.     exit(1);
  933. }
  934.  
  935. new_disk()
  936. {
  937.     char filespec[16];
  938.     int fd;
  939.  
  940.     /* is file spread over more than one disk? */
  941.     if(span_cnt > 0)
  942.     {
  943.     get_sqhdr();                    /* read file "RESTORE" header */
  944.     sqid.sq_fflag = SPAN_CHR;            /* mark as incomplete */
  945.     put_sqhdr();                    /* write it back */
  946.     fclose(outbuff);                /* and close file */
  947.     }
  948.     if(bkup_id != 0)                    /* indicate a full disk */
  949.     {
  950.     get_bkupid(tgt_drv);
  951.     bkid.bk_fflag = SPAN_CHR;            /* full disk */
  952.     strcpy(filespec,tgt_drv);
  953.     strcat(filespec,"SQZID.@@@");
  954.     fd = open(filespec,BWRITE);
  955.     write(fd,&bkid,sizeof(bkid));
  956.     close(fd);
  957.     }
  958.     bkup_id++;
  959.     dsk_reset(tgt_drv);
  960.     fprintf(stderr,"\n\n%cInsert backup diskette %02d in drive %s\n",'\007',bkup_id, tgt_drv);
  961.     fprintf(stderr,"Warning! Diskette files will be erased\n");
  962.     fprintf(stderr,"Strike any key when ready... ");
  963.     bdos(1,0);
  964.     dsk_reset(tgt_drv);
  965.     fprintf(stderr,"\n\n*** Squeezing files to diskette %02d ***\n",bkup_id);
  966.     all_unlink(tgt_drv);
  967.     put_bkupid(tgt_drv,bkup_id);
  968.  
  969.     /* get free space on new disk */
  970.     free_space = pspace(tgt_drv);
  971.  
  972.     /* if a file is spanning disks, write out new "RESTORE" header */
  973.     if(span_cnt > 0)
  974.     {
  975.     if((outbuff=fopen(outfile, FBRDWR)) == NULL) {
  976.         fprintf(stderr, "sq2: can't create %s\n", outfile);
  977.         fclose(inbuff);
  978.         return(FALSE);
  979.         }
  980.     wrt_sqhead(infile);
  981.     fprintf(stderr, "%s -> %s: analysing, squeezing, ", infile, outfile);
  982.     }
  983.     return(TRUE);
  984. }
  985.  
  986. /* get free space on drive */
  987.  
  988. #ifdef MSDC86
  989.  
  990. long pspace(drive)
  991. unsigned char *drive;
  992. {
  993.     struct { unsigned short ax,bx,cx,dx,si,di,ds,es; } srv;
  994.     extern int _sysvers;
  995.  
  996.     if((_sysvers & 0xff) == 0)
  997.     abort("Wrong DOS version");
  998.     if(*(drive+1) == ':')
  999.     srv.dx = (toupper(*drive)-64) & 0x0f;
  1000.     else
  1001.     srv.dx = 0;
  1002.     srv.ax = 0x3600;
  1003.     if(sysint21(&srv,&srv) & 1)
  1004.     abort("\nsq2: could not read disk space\n\n");
  1005.     return((long)srv.bx * (long)srv.cx * (long)srv.ax);
  1006. }
  1007.  
  1008. #endif
  1009.  
  1010. #ifdef MSDMSC
  1011.  
  1012. long pspace(drive)
  1013. unsigned char *drive;
  1014. {
  1015.     union REGS srv;
  1016.  
  1017.     if(*(drive+1) == ':')
  1018.     srv.x.dx = (toupper(*drive)-64) & 0x0f;
  1019.     else
  1020.     srv.x.dx = 0;
  1021.     srv.h.ah = 0x36;
  1022.     intdos(&srv, &srv);
  1023.     if(srv.x.cflag)
  1024.     {
  1025.     abort("sq2: Could not read disk space");
  1026.     exit(1);
  1027.     }
  1028.     return((long)srv.x.bx * (long)srv.x.cx * (long)srv.x.ax);
  1029. }
  1030. #endif
  1031.  
  1032. #ifdef CPMC86
  1033.  
  1034. long pspace(drive)
  1035. unsigned char *drive;
  1036. {
  1037.     unsigned int all_bx, all_es, dpb_bx, dpb_es;
  1038.     int cur_drv, bls, all_count, i, j;
  1039.  
  1040.     long tot_cap, cap_left;
  1041.  
  1042.     struct {
  1043.     unsigned int spt;
  1044.     unsigned char bsh;
  1045.     unsigned char blm;
  1046.     unsigned char exm;
  1047.     unsigned int dsm;
  1048.     unsigned int drm;
  1049.     unsigned char al0;
  1050.     unsigned char al1;
  1051.     unsigned int cks;
  1052.     unsigned int off;
  1053.     } dpb;
  1054.  
  1055.     unsigned char all_vec[200];
  1056.  
  1057.     struct { unsigned short ax,bx,cx,dx,si,di,ds,es; } srv;
  1058.     struct { unsigned int scs, sss, sds, ses; } rrv;
  1059.  
  1060.     /* get current drive for reset */
  1061.     srv.cx = 0x0019;
  1062.     sysint(224,&srv,&srv);
  1063.     cur_drv = (srv.ax & 0x00ff);
  1064.  
  1065.     /* select disk as per drive */
  1066.     srv.cx = 0x000e;
  1067.     srv.dx = 0x0000 | (toupper(*drive) - 65);
  1068.     sysint(224,&srv,&srv);
  1069.  
  1070.     /* get file allocation vector */
  1071.     srv.cx = 0x001b;
  1072.     sysint(224,&srv,&srv);
  1073.     all_bx = srv.bx;
  1074.     all_es = srv.es;
  1075.  
  1076.     /* get disk parameter block and copy to local structure */
  1077.     segread(&rrv);
  1078.     srv.cx = 0x001f;
  1079.     sysint(224,&srv,&srv);
  1080.     movblock(srv.bx,srv.es,&dpb,rrv.sds,sizeof(dpb));
  1081.  
  1082.     /* select disk as before */
  1083.     srv.cx = 0x000e;
  1084.     srv.dx = cur_drv;
  1085.     sysint(224,&srv,&srv);
  1086.  
  1087.     /* now work out free space */
  1088.     if(dpb.bsh==3 && dpb.blm == 7)
  1089.     bls = 1024;
  1090.     else if(dpb.bsh==4 && dpb.blm==15)
  1091.     bls = 2048;
  1092.     else if(dpb.bsh==5 && dpb.blm==31)
  1093.     bls = 4096;
  1094.     else if(dpb.bsh==6 && dpb.blm==63)
  1095.     bls = 8192;
  1096.     else if(dpb.bsh==7 && dpb.blm==127)
  1097.     bls = 16384;
  1098.     else
  1099.     abort("\n\nsq2: DPB error\n\n");
  1100.  
  1101.     /* this gives us total capacity on drive */
  1102.     tot_cap = (long) bls * (long) dpb.dsm;
  1103.  
  1104.     /* now - how much space used up re: alloc vector */
  1105.     movblock(all_bx,all_es, all_vec,rrv.sds, (dpb.dsm/8) + 1);
  1106.     for(all_count=0, i=0; i < ((dpb.dsm/8) +1); i++)
  1107.     {
  1108.     for(j=0; j<8; j++)
  1109.         {
  1110.         all_count += (all_vec[i] & 1);
  1111.         all_vec[i] >>= 1;
  1112.         }
  1113.     }
  1114.     cap_left = tot_cap - ((long) all_count * (long) bls);
  1115.     return(cap_left);
  1116. }
  1117.  
  1118. #endif
  1119.  
  1120. /* delete all files on drive - must be limited to A: or B: */
  1121.  
  1122. all_unlink(drive)
  1123. unsigned char *drive;
  1124. {
  1125.     char *next, *first, filespec[20];
  1126.     extern char *filedir(), *strchr();
  1127.  
  1128.     strcpy(filespec, drive);
  1129.     if(strchr(filespec,':') == NULL)
  1130.     strcat(filespec,":");
  1131.     strcat(filespec,"*.*");
  1132.     if((first = filedir(filespec,7)) == NULL)
  1133.     return(0);
  1134.     for(next = first; *next != NULL;)
  1135.     {
  1136.     strcpy(&filespec[2],next);
  1137.     unlink(filespec);
  1138.     next = next + strlen(next) + 1;
  1139.     }
  1140.     free(first);
  1141. }
  1142.  
  1143.  
  1144. /* make SQZID.@@@ */
  1145.  
  1146. put_bkupid(drive,id)
  1147. char *drive;
  1148. int id;
  1149. {
  1150.     char filespec[16];
  1151.     int fd;
  1152.  
  1153.     bkid.bk_fflag = 0x00;            /* last/incomplete disk */
  1154.     bkid.bk_seqn = id;
  1155.     strcpy(filespec,drive);
  1156.     strcat(filespec,"SQZID.@@@");
  1157.  
  1158. #ifdef MSDMSC
  1159.     fd = open(filespec,BWRITE,BPERMS);
  1160. #else
  1161.     fd = creat(filespec,BWRITE);
  1162. #endif
  1163.  
  1164.     write(fd,&bkid,sizeof(bkid));
  1165.     close(fd);
  1166. }
  1167.  
  1168. /* read SQZID.@@@ */
  1169.  
  1170. get_bkupid(drive)
  1171. char *drive;
  1172. {
  1173.     char filespec[16];
  1174.     int fd;
  1175.  
  1176.     strcpy(filespec,drive);
  1177.     strcat(filespec,"SQZID.@@@");
  1178.     if((fd = open(filespec,BREAD)) < 0)
  1179.     return(0);
  1180.     read(fd,&bkid,sizeof(bkid));
  1181.     close(fd);
  1182.     return((INT) bkid.bk_seqn);
  1183. }
  1184.  
  1185. hasbad(trg)
  1186. char *trg;
  1187. {
  1188.     char c;
  1189.  
  1190.     while (c = *trg++)
  1191.     if (c == '*' || c == '?' || c == '.' || c == '\\' || c == '/')
  1192.         return TRUE;
  1193.     return FALSE;
  1194. }
  1195.  
  1196. /* write out standard 128 byte "RESTORE" header */
  1197.  
  1198. wrt_sqhead(infile)
  1199. char *infile;
  1200. {
  1201.     char *strchr(), *col_ptr;
  1202.  
  1203.     sqid.sq_fflag = FIN_CHR;
  1204.     sqid.sq_span = span_cnt;
  1205.     if((col_ptr = strchr(infile,':')) != NULL)
  1206.     {
  1207.     strcpy(sqid.sq_path, ++col_ptr);
  1208.     }
  1209.     else
  1210.     strcpy(sqid.sq_path,infile);
  1211.     put_sqhdr();
  1212.     free_space -= ((long) sizeof(sqid));
  1213. }
  1214.  
  1215. /* write "RESTORE" header */
  1216.  
  1217. put_sqhdr()
  1218. {
  1219.     long fseek();
  1220.  
  1221.     fseek(outbuff,0L,0);
  1222.     fwrite(&sqid,sizeof(sqid),1,outbuff);
  1223. }
  1224.  
  1225. /* read "RESTORE" header */
  1226.  
  1227. get_sqhdr()
  1228. {
  1229.     long fseek();
  1230.  
  1231.     fseek(outbuff,0L,0);
  1232.     fread(&sqid,sizeof(sqid),1,outbuff);
  1233. }
  1234.  
  1235.  
  1236. /*    get file directory
  1237. */
  1238.  
  1239. #ifdef MSDMSC
  1240.  
  1241. unsigned char *malloc();
  1242. unsigned char *realloc();
  1243.  
  1244. struct ff_str {
  1245.   char dummy[21];        /* reserved for dos */
  1246.   unsigned char attribute;    /* returned attribute */
  1247.   unsigned time;
  1248.   unsigned date;
  1249.   long size;            /* size of file */
  1250.   unsigned char fn[13];     /* string containing the filename */
  1251. };
  1252.  
  1253. char *filedir(filename,mode)
  1254. unsigned char *filename;
  1255. unsigned mode;
  1256. {
  1257.     union REGS srvi, srvo;
  1258.     struct SREGS segregs;
  1259.     unsigned int ds, dx;
  1260.     struct ff_str ff_area;
  1261.     unsigned char *result;
  1262.     int reslen=0;
  1263.  
  1264. #ifdef M_I86LM                    /* large model ? */
  1265.     ds = FP_SEG(filename);
  1266.     dx = FP_OFF(filename);
  1267. #else
  1268.     segread(&segregs);                /* get ds value */
  1269.     dx = (unsigned) filename;
  1270.     ds = segregs.ds;
  1271. #endif
  1272.  
  1273.     srvi.x.dx = (unsigned int) &ff_area;     /* set DTA */
  1274.     srvi.h.ah = 0x1a;
  1275.     intdosx(&srvi, &srvo, &segregs);
  1276.  
  1277.     srvi.x.cx = mode;                 /* set search modes */
  1278.     srvi.h.ah = 0x4e;                 /* find first */
  1279.     srvi.x.dx = dx;
  1280.     segregs.ds = ds;
  1281.     intdosx(&srvi, &srvo, &segregs);
  1282.     if(srvo.x.cflag)
  1283.     return(NULL);
  1284.     if((result = malloc(strlen(ff_area.fn) + 1)) == NULL)
  1285.     return(result);
  1286.     reslen = strlen(ff_area.fn) + 1;
  1287.     strcpy(result, ff_area.fn);
  1288.  
  1289.     srvi.h.ah = 0x4f;                /* find next */
  1290.     for(;;)
  1291.     {
  1292.     segregs.ds = ds;
  1293.     intdosx(&srvi, &srvo, &segregs);
  1294.     if(srvo.x.cflag)
  1295.         {
  1296.         result = realloc(result, reslen + 1);
  1297.         if(result != NULL)
  1298.         *(result + reslen) = '\0';
  1299.         return(result);
  1300.         }
  1301.     result=realloc(result,reslen+strlen(ff_area.fn)+1);
  1302.     if(result==NULL)
  1303.         return NULL;            /* no memory left */
  1304.     strcpy(result+reslen,ff_area.fn);
  1305.     reslen += (strlen(ff_area.fn) + 1);
  1306.     }
  1307. }
  1308.  
  1309. #endif
  1310.  
  1311. #ifdef CPMC86
  1312.  
  1313. unsigned char *malloc();
  1314. unsigned char *realloc();
  1315.  
  1316. char *filedir(filename)
  1317. unsigned char *filename;
  1318. {
  1319.     char fcb[36];
  1320.     char tmpfile[30];
  1321.     char dma[128];
  1322.     struct { unsigned int ax,bx,cx,dx,si,di,ds,es;} srvi, srvo;
  1323.     struct { unsigned int scs, sss, sds, ses;} segs;
  1324.     unsigned int dataseg, fcboff;
  1325.     unsigned char *result;
  1326.     int reslen;
  1327.     int i;
  1328.  
  1329.     reslen = 0;
  1330.     segread(&segs);                 /* get ds value */
  1331.  
  1332.     fcboff = (unsigned) &fcb[0];         /* get fcb offset */
  1333.     dataseg = segs.sds;
  1334.  
  1335.     srvi.dx = (unsigned int) &dma[0];         /* set DMA */
  1336.     srvi.cx = 0x001a;                 /* set offset */
  1337.     sysint(224, &srvi, &srvo);
  1338.     srvi.dx = dataseg;
  1339.     srvi.cx = 0x0033;                 /* set segment */
  1340.     sysint(224, &srvi, &srvo);
  1341.  
  1342.     srvi.cx = 0x0011;                 /* find first */
  1343.     srvi.dx = fcboff;
  1344.     srvi.ds = dataseg;
  1345.     if(!setfcb(fcb, filename))
  1346.     return(NULL);
  1347.  
  1348.     sysint(224, &srvi, &srvo);
  1349.     if((srvo.ax & 0x00ff) == 255)
  1350.     return(NULL);
  1351.     hackname(tmpfile, dma + (srvo.ax * 32));
  1352.  
  1353.     if((result = malloc(strlen(tmpfile) + 1)) == NULL)
  1354.     return(result);
  1355.     reslen = strlen(tmpfile) + 1;
  1356.     strcpy(result, tmpfile);
  1357.  
  1358.     srvi.cx = 0x0012;                /* find next */
  1359.     for(;;)
  1360.     {
  1361.     sysint(224, &srvi, &srvo);
  1362.     if(srvo.ax == 255)
  1363.         {
  1364.         result = realloc(result, reslen + 1);
  1365.         if(result != NULL)
  1366.         *(result + reslen) = '\0';
  1367.         return(result);
  1368.         }
  1369.     hackname(tmpfile, dma + (srvo.ax * 32));
  1370.     result=realloc(result,reslen+strlen(tmpfile)+1);
  1371.     if(result==NULL)
  1372.         return NULL;            /* no memory left */
  1373.     strcpy(result+reslen,tmpfile);
  1374.     reslen += (strlen(tmpfile) + 1);
  1375.     }
  1376. }
  1377.  
  1378. #endif
  1379.  
  1380. #ifdef CPMC86
  1381.  
  1382. dsk_reset(drv)                    /* oh how I love CP/M!! */
  1383. char *drv;
  1384. {
  1385.     struct { unsigned int ax,bx,cx,dx,si,di,ds,es;} srvi, srvo;
  1386.     unsigned int vector;
  1387.     int drvno;
  1388.  
  1389.     drvno = toupper(*drv) - 65;
  1390.     vector = 0x0001;
  1391.     if(drvno != 0)
  1392.     vector <<= drvno;
  1393.     srvi.dx = vector;
  1394.     srvi.cx = 0x0025;                /* disk reset fn */
  1395.     sysint(224, &srvi, &srvo);
  1396.     return(srvo.ax);
  1397. }
  1398.  
  1399. #else
  1400.  
  1401. dsk_reset(drv)
  1402. char *drv;
  1403. {
  1404.     return(0);
  1405. }
  1406.  
  1407. #endif
  1408.  
  1409. #ifdef MSDMSC
  1410. abort(string)
  1411. char *string;
  1412. {
  1413.     fprintf(stderr,"\n\n%s\n\n", string);
  1414.     exit(1);
  1415. }
  1416. #endif
  1417.  
  1418.